import types, os, sys, traceback
from ext import *


class Redirect(Exception):
    
    def __init__(self, path):
        Exception.__init__(self)
        self.path = path
        
class InternalServerError(Exception):
    
    def __init__(self, msg='internal server error'):
        Exception.__init__(self)
        self.message = msg 


class PathRouter(object):

    '''
    This class routes the Requests from the server to the plugins.
    '''

    def __init__(self):
        self.__routes = {}
        self.__reserved = []

    def dispatch_request(self, path, header, request):
        '''
        let a plugin resolve a request. An IndexError is thrown if the path was not found.
        This means a 404 error.
        '''
        
        body = ''
        response_headers = {'Content-Type': 'text/html'}
        method = None
        longest = ''
        length = 0
        for route in self.__routes.keys():
            
            if route.endswith('*'):
                _route = route[:-1]
                if path.startswith(_route):
                    if len(_route) > length:
                        length = len(_route)
                        longest = self.__routes[route]
        if longest:
            method = longest
        else:
            if not path in self.__routes.keys():
                response_headers['Content-Type'] = 'text/plain'
                return '404 NOT FOUND', response_headers, '404'
            method = self.__routes[path]
        try:
            body = method(header, request, response_headers)
            return '200 OK', response_headers, body
        except Redirect:
            r = sys.exc_info()[1]
            response_headers['Location'] = r.path
            return '301 Moved Permanently', response_headers, body
        except InternalServerError:
            i = sys.exc_info()[1]
            body = i.message
            response_headers['Content-Type'] = 'text/plain'
            return '500 INTERNAL SERVER ERROR', response_headers, body
        except:
            body = 'Internal Server Error: ' + traceback.format_exc()
            response_headers['Content-Type'] = 'text/plain'
            return '500 INTERNAL SERVER ERROR', response_headers, body

    def add_reserved(self, reserved):
        self.__reserved.extend(reserved)

    def add_route(self, path, callback):
        if type(path) != str:
            raise TypeError('path must be a string')
        elif not callable(callback):
            raise TypeError('callback must be callable')
        if path in self.__routes.keys() and path in self.__reserved:
            raise RuntimeError('{0} is reserved and cannot be overridden'.format(path))
        self.__routes[path] = callback

    def route(self, path):
        def inner(callback):
            self.add_route(path, callback)
        return inner

    def delete_route(self, path):
        if not path in self.__routes.keys():
            raise IndexError(interpolate('no path: #path'))
        del self.__routes[path]
